// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;

namespace SnapObjects.Data
{
    internal class ModelSelectBuilder
    {
        private readonly DataContext _dataContext;

        public ModelSelectBuilder(DataContext dataContext)
        {
            _dataContext = dataContext;
        }

        public string GetSqlText(
            Type modelType,
            QuerySelector querySelector,
            string expression,
            out IReadOnlyList<ISqlParameter> defindedParameters)
        {
            var sqlText = String.Empty;

            switch (querySelector)
            {
                case QuerySelector.Select:
                    var modelQueryBuilder = this.GetModelQueryBuilder(modelType);
                    sqlText = modelQueryBuilder.SqlQueryBuilder.ToSqlString(_dataContext);
                    defindedParameters = modelQueryBuilder.GetSqlParameters();
                    break;
                case QuerySelector.SelectByKey:
                    sqlText = this.BuildSelectByKey(modelType,
                                out var sqlParameters);

                    defindedParameters = sqlParameters;
                    break;
                case QuerySelector.SelectCount:
                case QuerySelector.SelectDistinctCount:
                case QuerySelector.Scalar:
                case QuerySelector.ScalarByKey:
                case QuerySelector.SelectMax:
                case QuerySelector.SelectMin:
                case QuerySelector.SelectAvg:
                case QuerySelector.SelectSum:
                    var builder = this.GetFetchOneBuilder(
                                    modelType,
                                    querySelector,
                                    expression,
                                    out var sqlParameters2);

                    sqlText = builder.ToSqlString(_dataContext);
                    defindedParameters = sqlParameters2;
                    break;
                default:
                    defindedParameters = new List<ISqlParameter>();
                    break;
            }

            return sqlText;
        }

        public ModelQueryBuilder GetModelQueryBuilder(
            Type modelType,
            string selectName = null)
        {
            var modelQueryBuilder = new ModelQueryBuilder(_dataContext, modelType);
            modelQueryBuilder.BuildModelSelect(selectName);

            return modelQueryBuilder;
        }

        public string BuildSelectIgnoreWhere(Type modelType)
        {
            var modelQueryBuilder = new ModelQueryBuilder(_dataContext, modelType);

            return modelQueryBuilder.BuildSelectIgnoreWhere().ToSqlString(_dataContext);
        }

        public string BuildSelectByKey(
            Type modelType,
            out IReadOnlyList<ISqlParameter> definedParameters)
        {
            var builder = this.GetSelectByKeyBuilder(
                modelType,
                out var sqlParameters);

            var sqlText = builder.ToSqlString(_dataContext);
            definedParameters = sqlParameters;

            return sqlText;
        }

        private ISqlQueryBuilder GetSelectByKeyBuilder(
            Type modelType,
            out IReadOnlyList<ISqlParameter> sqlParameters)
        {
            var modelQueryBuilder = new ModelQueryBuilder(_dataContext, modelType);
            var builder = modelQueryBuilder.BuildSelectByKey();
            sqlParameters = modelQueryBuilder.GetSqlParameters();

            return builder;
        }

        public string BuildSelectCountByKey(
            Type modelType,
            out IReadOnlyList<ISqlParameter> definedParameters)
        {
            var builder = this.GetSelectCountByKeyBuilder(
                            modelType,
                            out var sqlParameters);

            var sqlText = builder.ToSqlString(_dataContext);
            definedParameters = sqlParameters;

            return sqlText;
        }

        private ISqlQueryBuilder GetSelectCountByKeyBuilder(
            Type modelType,
            out IReadOnlyList<ISqlParameter> sqlParameters)
        {
            var modelQueryBuilder = new ModelQueryBuilder(_dataContext, modelType);
            var builder = modelQueryBuilder.BuildFetchOneByKey("count(1)");
            sqlParameters = modelQueryBuilder.GetSqlParameters();

            return builder;
        }

        public string BuildFetchOne(
            Type modelType,
            QuerySelector modelSql,
            string expression,
            out IReadOnlyList<ISqlParameter> defindedParameters)
        {
            var builder = this.GetFetchOneBuilder(
                                modelType,
                                modelSql,
                                expression,
                                out var sqlParameters);

            var sqlText = builder.ToSqlString(_dataContext);
            defindedParameters = sqlParameters;

            return sqlText;
        }

        private ISqlQueryBuilder GetFetchOneBuilder(
            Type modelType,
            QuerySelector querySelector,
            string expression,
            out IReadOnlyList<ISqlParameter> sqlParameters)
        {
            if (querySelector != QuerySelector.SelectCount &&
                String.IsNullOrWhiteSpace(expression))
            {
                throw new InvalidOperationException("Select expression cannot be null or white space.");
            }

            ISqlQueryBuilder sqlQueryBuilder = null;
            var modelQueryBuilder = new ModelQueryBuilder(_dataContext, modelType);

            switch (querySelector)
            {
                case QuerySelector.SelectCount:
                    sqlQueryBuilder = modelQueryBuilder.BuildFetchOne("count(1)");

                    sqlParameters = modelQueryBuilder.GetSqlParameters();
                    break;
                case QuerySelector.SelectDistinctCount:
                    sqlQueryBuilder = modelQueryBuilder.BuildFetchOne(
                                        "count(DISTINCT " + modelQueryBuilder.GetFetchOneExpression(expression) + ")");

                    sqlParameters = modelQueryBuilder.GetSqlParameters();
                    break;
                case QuerySelector.Scalar:
                    sqlQueryBuilder = modelQueryBuilder.BuildFetchOne(
                                        modelQueryBuilder.GetFetchOneExpression(expression));

                    sqlParameters = modelQueryBuilder.GetSqlParameters();
                    break;
                case QuerySelector.ScalarByKey:
                    sqlQueryBuilder = modelQueryBuilder.BuildFetchOneByKey(
                                        modelQueryBuilder.GetFetchOneExpression(expression));

                    sqlParameters = modelQueryBuilder.GetSqlParameters(); ;
                    break;
                case QuerySelector.SelectMax:
                    sqlQueryBuilder = modelQueryBuilder.BuildFetchOne(
                                        "Max(" + modelQueryBuilder.GetFetchOneExpression(expression) + ")");

                    sqlParameters = modelQueryBuilder.GetSqlParameters();
                    break;
                case QuerySelector.SelectMin:
                    sqlQueryBuilder = modelQueryBuilder.BuildFetchOne(
                                        "Min(" + modelQueryBuilder.GetFetchOneExpression(expression) + ")");

                    sqlParameters = modelQueryBuilder.GetSqlParameters();
                    break;
                case QuerySelector.SelectAvg:
                    sqlQueryBuilder = modelQueryBuilder.BuildFetchOne(
                                        "Avg(" + modelQueryBuilder.GetFetchOneExpression(expression) + ")");

                    sqlParameters = modelQueryBuilder.GetSqlParameters();
                    break;
                case QuerySelector.SelectSum:
                    sqlQueryBuilder = modelQueryBuilder.BuildFetchOne(
                                        "Sum(" + modelQueryBuilder.GetFetchOneExpression(expression) + ")");

                    sqlParameters = modelQueryBuilder.GetSqlParameters();
                    break;
                default:
                    sqlParameters = new List<ISqlParameter>();
                    break;
            }

            return sqlQueryBuilder;
        }
    }
}
